<?php

class XMLImporter
{
    // Database configuration
    private $dbConfig = Array();
    private $fieldsMap = Array();
    private $connection;
    private $xmlFilePath;
    private $xml;
    private $downloadFilesFolder;
    private $rootItem;

    const ERROR_DATABASE_CONFIGURATION_NOT_PROVIDED = "Database configuration is not provided for XMLImporter!";
    const ERROR_XML_DATABASE_FIELDS_MAP_NOT_PROVIDED = "XML to database fields map is not provided for XMLImporter!";
    const ERROR_XML_FILE_PATH_NOT_PROVIDED = "XML file path is not provided for XMLImporter!";
    const ERROR_XML_ROOT_ITEM_NOT_PROVIDED = "XML file root item name is not provided!";
    const ERROR_READ_XML_FILE = "Can't read XML file!";

    public function __construct($dbConfig, $fieldsMap, $xmlFilePath, $downloadFilesFolder, $rootItem)
    {
        if (isset($dbConfig)) {
            $this->dbConfig = $dbConfig;
            $this->connectToDatabase();
        } else {
            die(self::ERROR_DATABASE_CONFIGURATION_NOT_PROVIDED);
        }

        if (isset($fieldsMap)) {
            $this->fieldsMap = $fieldsMap;
        } else {
            die(self::ERROR_XML_DATABASE_FIELDS_MAP_NOT_PROVIDED);
        }

        if (isset($xmlFilePath)) {
            $this->xmlFilePath = $this->getXmlFilePath($xmlFilePath);
//            $this->xml = $this->loadXMLFile($this->xmlFilePath);
        } else {
            die(self::ERROR_XML_FILE_PATH_NOT_PROVIDED);
        }

        if (isset($downloadFilesFolder)) {
            $this->downloadFilesFolder = $downloadFilesFolder;
        }

        if (isset($rootItem)) {
            $this->rootItem = $rootItem;
        } else {
            die(self::ERROR_XML_ROOT_ITEM_NOT_PROVIDED);
        }
    }

    private function connectToDatabase()
    {
        // Create Database connection
        $this->connection = mysqli_connect($this->dbConfig["host"], $this->dbConfig["user"], $this->dbConfig["password"], $this->dbConfig["database"]);

        // Check connection
        if (mysqli_connect_errno()) {
            die("Failed to connect to MySQL: " . mysqli_connect_error());
        }
    }

    /**
     * @param $file_name
     * @return SimpleXMLElement
     */
    public static function getXml($file_name, $rootItem)
    {
        $xml = new XMLReader();
        $xml->open($file_name);

        while ($xml->read()) {
            if ($rootItem === $xml->name) {
                return $xml->readOuterXml();
            }
        }
        return "No $rootItem root element for $file_name!";
    }

    /**
     * For every node that starts with $startNode and ends with $endNode call $callback
     * with the string as an argument
     *
     * Note: Sometimes it returns two nodes instead of a single one, this could easily be
     * handled by the callback though. This function primary job is to split a large file
     * into manageable XML nodes.
     *
     * the callback will receive one parameter, the XML node(s) as a string
     *
     * @param resource $handle - a file handle
     * @param string $startNode - what is the start node name e.g <item>
     * @param string $endNode - what is the end node name e.g </item>
     * @param callable $callback - an anonymous function
     */
    function nodeStringFromXMLFile($handle, $startNode, $endNode, $callback = null)
    {
        $cursorPos = 0;
        while (true) {
            // Find start position
            $startPos = $this->getPos($handle, $startNode, $cursorPos);
            // We reached the end of the file or an error
            if ($startPos === false) {
                break;
            }
            // Find where the node ends
            $endPos = $this->getPos($handle, $endNode, $startPos) + mb_strlen($endNode);
            // Jump back to the start position
            fseek($handle, $startPos);
            // Read the data
            $data = fread($handle, ($endPos - $startPos));
            // pass the $data into the callback
            $callback($data);
            // next iteration starts reading from here
            $cursorPos = ftell($handle);
        }
    }

    /**
     * This function will return the first string it could find in a resource that matches the $string.
     *
     * By using a $startFrom it recurses and seeks $chunk bytes at a time to avoid reading the
     * whole file at once.
     *
     * @param resource $handle - typically a file handle
     * @param string $string - what string to search for
     * @param int $startFrom - strpos to start searching from
     * @param int $chunk - chunk to read before rereading again
     * @return int|bool - Will return false if there are EOL or errors
     */
    function getPos($handle, $string, $startFrom = 0, $chunk = 1024)
    {
        // Set the file cursor on the startFrom position
        fseek($handle, $startFrom, SEEK_SET);
        // Read data
        $data = fread($handle, $chunk);
        // Try to find the search $string in this chunk
        $stringPos = mb_strpos($data, $string);
        // We found the string, return the position
        if ($stringPos !== false) {
            return $stringPos + $startFrom;
        }
        // We reached the end of the file
        if (feof($handle)) {
            return false;
        }
        // Recurse to read more data until we find the search $string it or run out of disk
        return $this->getPos($handle, $string, $chunk + $startFrom);
    }

    /**
     * Turn a string version of XML and turn it into an array by using the
     * SimpleXML
     *
     * @param string $nodeAsString - a string representation of a XML node
     * @return array
     */
    function getArrayFromXMLString($nodeAsString)
    {
        $simpleXML = simplexml_load_string($nodeAsString);
        if (libxml_get_errors()) {
            user_error('Libxml throws some errors.', implode(',', libxml_get_errors()));
        }
        return $this->simplexml2array($simpleXML);
    }

    /**
     * Turns a SimpleXMLElement into an array
     *
     * @param SimpleXMLelem $xml
     * @return array
     */
    function simplexml2array($xml)
    {
        if (is_object($xml) && get_class($xml) == 'SimpleXMLElement') {
            $attributes = $xml->attributes();
            foreach ($attributes as $k => $v) {
                $a[$k] = (string)$v;
            }
            $x = $xml;
            $xml = get_object_vars($xml);
        }

        if (is_array($xml)) {
            if (count($xml) == 0) {
                return (string)$x;
            }
            $r = array();
            foreach ($xml as $key => $value) {
                $r[$key] = $this->simplexml2array($value);
            }
            // Ignore attributes
            if (isset($a)) {
                $r['@attributes'] = $a;
            }
            return $r;
        }
        return (string)$xml;
    }

    public function parseLargeFile($table, $idName)
    {
        $fields = array_keys($this->fieldsMap);
        $sql = $this->getBaseInsertStatement($table, $fields);
        $xmlFields = array_values($this->fieldsMap);

        $xml = new XMLReader();
        $xml->open($this->xmlFilePath);

        $uniqueItemsWrapper = array();
        while ($xml->read()) {
            if ($this->rootItem === $xml->name) {
//                echo $xml->name;
//                echo $xml->readInnerXml();
//                echo $xml->readOuterXml();

                $xmlItem = simplexml_load_string($xml->readOuterXml());
                if (libxml_get_errors()) {
                    user_error('Libxml throws some errors.', implode(',', libxml_get_errors()));
                }

                $itemWrapper = new ItemWrapper($xmlItem, null);
                $item = $this->toString($itemWrapper, $xmlFields);
                $uniqueItemsWrapper[$item] = $itemWrapper;

                $xml->next();
            }
        }

        $uniqueItemsWrapper = array_values($uniqueItemsWrapper);

        $sql .= $this->parseItems($uniqueItemsWrapper, $this->fieldsMap);

        $this->executeUpdate($sql, $this->fieldsMap);

        $firstItemId = mysqli_insert_id($this->connection);
        $lastItemId = $firstItemId + mysqli_affected_rows($this->connection) - 1;

        $addedItems = $this->getAddedItems($table, $idName, $firstItemId, $lastItemId);
        return array($this->getItemWrappers($uniqueItemsWrapper, $addedItems, $this->getKey($this->fieldsMap)), $firstItemId, $lastItemId);

        // Get the nodestring incrementally from the xml file by defining a callback
        // In this case using a anon function.
//        $this->nodeStringFromXMLFile($handle, '<' . $this->rootItem . '>', '</' . $this->rootItem . '>', function ($nodeText) {
        // Transform the XMLString into an array and
//            print_r($this->getArrayFromXMLString($nodeText));
//        });
//        fclose($handle);
    }

    public function parseFile($table, $idName)
    {
        $fields = array_keys($this->fieldsMap);
        $sql = $this->getBaseInsertStatement($table, $fields);

        $xmlFields = array_values($this->fieldsMap);
        $productList = $this->getProductList($this->xml, $this->rootItem);
        $uniqueItemsWrapper = array();
        foreach ($productList as $product) {
            $itemWrapper = new ItemWrapper($product, null);
            $item = $this->toString($itemWrapper, $xmlFields);
            $uniqueItemsWrapper[$item] = $itemWrapper;
        }

        $uniqueItemsWrapper = array_values($uniqueItemsWrapper);

        $sql .= $this->parseItems($uniqueItemsWrapper, $this->fieldsMap);

        $this->executeUpdate($sql, $this->fieldsMap);

        $firstItemId = mysqli_insert_id($this->connection);
        $lastItemId = $firstItemId + mysqli_affected_rows($this->connection) - 1;

        $addedItems = $this->getAddedItems($table, $idName, $firstItemId, $lastItemId);
        return array($this->getItemWrappers($uniqueItemsWrapper, $addedItems, $this->getKey($this->fieldsMap)), $firstItemId, $lastItemId);
    }

    private function getAddedItems($table, $idName, $firstItemId, $lastItemId)
    {
        $sql = "SELECT * FROM $table WHERE $idName BETWEEN $firstItemId AND $lastItemId";
        return $this->executeQuery($sql);
    }

    private function getItemWrappers($uniqueItemsWrapper, $addedItems, $key)
    {
        $result = array();
        while ($row = mysqli_fetch_assoc($addedItems)) {
            $result[] = $this->getXMLItem($uniqueItemsWrapper, $row[$key], $key);
        }
        return $result;
    }

    private function getXMLItem($uniqueItemsWrapper, $databaseItemValue, $key)
    {
        foreach ($uniqueItemsWrapper as $item) {
            $itemValue = $item->fields[$key];
            if ($itemValue->getValue() == $databaseItemValue) {
                return $item;
            }
        }
    }

    private function getKey($fieldsMap)
    {
        foreach ($fieldsMap as $databaseFiled => $xmlField) {
            if (array_key_exists("key", $xmlField)) {
                return $databaseFiled;
            }
        }
    }

    /**
     * @param $uniqueItemsWrapper
     * @param $fieldsMap
     * @return string
     */
    private function parseItems($uniqueItemsWrapper, $fieldsMap)
    {
        $sql = "";
        foreach ($uniqueItemsWrapper as $itemWrapper) {
            $fields = array();
            foreach ($fieldsMap as $databaseFiled => $xmlField) {
                $value = $this->getValue($itemWrapper, $xmlField);
                $dependsOn = array_key_exists("dependsOn", $xmlField) ? $xmlField["dependsOn"] : null;
                $fields[$databaseFiled] = new Field($databaseFiled, $value, $dependsOn);
            }

            $data = $this->processDependencies($fields);

            $sql .= "(" . join(", ", $data) . "),\n";

            $itemWrapper->fields = $fields;
        }
        return $sql;
    }

    private function executeQuery($query)
    {
        $result = mysqli_query($this->connection, $query);
        if (!$result) {
            die('Error: ' . mysqli_error($this->connection));
        }
        return $result;
    }

    public static function loadXMLItem($xmlFilePath, $rootItem)
    {
        return self::getXml(self::getXmlFilePath($xmlFilePath), $rootItem);
    }

//    public static function loadXMLFile($xmlFilePath)
//    {
//        $xml = simplexml_load_file($xmlFilePath);
//        if ($xml === false) {
//            echo "Failed loading XML</br>";
//            self::showErrors();
//        } else {
//            return $xml;
//        }
//        $pathinfo = pathinfo(parse_url($xmlFilePath, PHP_URL_PATH));
//        if (strtolower($pathinfo["extension"]) !== "zip") {
//            return self::getXml($xmlFilePath);
//        }
//        // assuming file.zip is in the same directory as the executing script.
//        $temp_file = tempnam(sys_get_temp_dir(), 'xml_');
//        file_put_contents($temp_file, fopen($xmlFilePath, 'r'));
//
//        // get the absolute path to $file
//        $path = pathinfo(realpath($temp_file), PATHINFO_DIRNAME);
//        $urlPath = parse_url($xmlFilePath, PHP_URL_PATH);
//        $file_name = basename($urlPath);
//
//        $zip = new ZipArchive;
//        $res = $zip->open($temp_file);
//        if ($res === TRUE) {
//            // extract it to the path we determined above
//            $zip->extractTo($path, array($file_name));
//            $zip->close();
//            echo "WOOT! $temp_file extracted to $path";
//        } else {
//            echo "Doh! I couldn't open $temp_file";
//            die(self::ERROR_READ_XML_FILE . " $xmlFilePath");
//        }
//        return self::getXml($file_name);

    // Raising this value may increase performance
//                $buffer_size = 8192; // read 8kb at a time
//                $out_file_name = $this->downloadFilesFolder . str_replace('.gz', '', $tmpFile);
//                // Open our files (in binary mode)
//                $file = gzopen($file, 'rb');
//                $out_file = fopen($out_file_name, 'wb');
//                // Keep repeating until the end of the input file
//                while (!gzeof($file)) {
//                    // Read buffer-size bytes
//                    // Both fwrite and gzread and binary-safe
//                    fwrite($out_file, gzread($file, $buffer_size));
//                }
//                // Files are done, close files
//                fclose($out_file);
//                gzclose($file);
//                $this->xmlFilePath = $out_file;
//        }

//        $xml = simplexml_load_file($this->xmlFilePath);
//        if ($xml === false) {
//            echo "Failed loading XML</br>";
//            $this->showErrors();
//        } else {
//            $this->xml = $xml;
//            echo "File " . $xml->getName() . " has been loaded</br>";
//        }
//    }

    public static function showErrors()
    {
        foreach (libxml_get_errors() as $error) {
            echo "\t", $error->message;
        }
    }

    private function getValue($itemWrapper, $xmlField)
    {
        $type = $xmlField["type"];
        $name = $xmlField["value"];
        $default = array_key_exists("default", $xmlField) ? $xmlField["default"] : null;
        $action = array_key_exists("action", $xmlField) ? $xmlField["action"] : null;
        $itemIndex = array_key_exists("index", $xmlField) ? $xmlField["index"] : null;
        $var = null;
        if (!strpos($name, "->")) {
            $var = $itemWrapper->item;
        } else {
            $explode = explode("->", $name);
            for ($i = 0; $i < count($explode) - 1; $i++) {
                $var = isset($var) ? $var->$explode[$i] : $itemWrapper->item->$explode[$i];
            }
            $name = $explode[count($explode) - 1];
        }
        $result = $this->getDefaultValue($this->getValueByType($var, $name, $type, $itemIndex), $default);
        if (isset($action)) {
            $var1 = $this->$action($result, $itemWrapper);
            return preg_replace('/\p{C}+/u', "", $var1);
        }
        return preg_replace('/\p{C}+/u', "", $result);
    }

    // Move callbacks to separate class!
    public function getCategoryByTitle($title, $itemWrapper)
    {
        $result = $this->executeQuery("SELECT cat_id FROM tb_category WHERE cat_name = '$title'");
        if ($result->num_rows > 0) {
            $categoryRow = $result->fetch_assoc();
            return $categoryRow['cat_id'];
        }
        $this->executeQuery("INSERT INTO tb_category (cat_name, parent_id, active, cat_keyword) VALUES ('$title', 0, 1, '')");
        return mysqli_insert_id($this->connection);
    }

    public function getSubcategoryByTitle($title, $itemWrapper)
    {
        $primaryCategory = $this->getValue($itemWrapper, Array(Type::TYPE => ValueType::TAG,
            "value" => "category->primary",
            "action" => 'getCategoryByTitle'));

        $result = $this->executeQuery("SELECT cat_id FROM tb_category WHERE cat_name = '$title' AND parent_id = '$primaryCategory'");
        if ($result->num_rows > 0) {
            $categoryRow = $result->fetch_assoc();
            return $categoryRow['cat_id'];
        }
        $this->executeQuery("INSERT INTO tb_category (cat_name, parent_id, active, cat_keyword) VALUES ('$title', '$primaryCategory', 1, '')");
        return mysqli_insert_id($this->connection);
    }

    public function getGrouponSubcategoryByTitle($title, $itemWrapper)
    {
        $primaryCategory = $this->getValue($itemWrapper, Array(Type::TYPE => ValueType::TAG_INDEX,
            "value" => "dealTypes->dealType->id",
            "index" => "0",
            "action" => "getCategoryByTitle"));

        $result = $this->executeQuery("SELECT cat_id FROM tb_category WHERE cat_name = '$title' AND parent_id = '$primaryCategory'");
        if ($result->num_rows > 0) {
            $categoryRow = $result->fetch_assoc();
            return $categoryRow['cat_id'];
        }
        $this->executeQuery("INSERT INTO tb_category (cat_name, parent_id, active, cat_keyword) VALUES ('$title', '$primaryCategory', 1, '')");
        return mysqli_insert_id($this->connection);
    }

    public function getAffilateLink($result, $itemWrapper)
    {
        $disallowed = array('http://', 'https://');
        foreach ($disallowed as $item) {
            if (strpos($result, $item) === 0) {
                return str_replace($item, '', $result);
            }
        }
        return $result;
    }

    private function toString($item, $fields)
    {
        return uniqid("", true);
//        $result = "";
//        foreach ($fields as $field) {
//            $result .= $this->getValue($item, $field);
//        }
//        return $result . uniqid();
    }

    /**
     * @param $fieldsMap
     * @internal param $xmlFields
     * @return mixed
     */
    private function getSimpleName($fieldsMap)
    {
        foreach ($fieldsMap as $databaseField => $xmlField) {
            return "`" . $databaseField . "` = `" . $databaseField . "`";
        }
        return "";
    }

    private function getValueByType($item, $name, $type, $index = NULL)
    {
        if ($type == ValueType::ATTRIBUTE) {
            return $item[$name];
        }
        if ($type == ValueType::TAG) {
            return $item->$name;
        }
        if ($type == ValueType::TAG_INDEX) {
            $i = 0;
            foreach ($item as $itemNum) {
                if ($i == $index) {
                    return $itemNum->$name;
                }
                $i++;
            }
            return $item->$name[$index];
        }
        if ($type == ValueType::VALUE) {
            return $name;
        }
    }

    /**
     * @param $fields
     * @return string
     */
    private function getBaseInsertStatement($table, $fields)
    {
        $itemValues = array();
        foreach ($fields as $value) {
            $itemValues[] = "`" . $value . "`";
        }
        return "INSERT INTO " . $table . " (" . join(', ', $itemValues) . ") VALUES ";
    }

    /**
     * @return string
     */
    private function getFinishInsertStatement($fieldsMap)
    {
        return " ON DUPLICATE KEY UPDATE " . $this->getSimpleName($fieldsMap);
    }

    public function mapAdditionalTable($uniqueItemsWrapper, $firstItemId, $lastItemId, $fieldsMap, $table, $key)
    {
        echo "Mapping additional table: $table \n";
        echo "First item ID: $firstItemId \n";
        echo "Last item ID: $lastItemId \n";
//        print_r($uniqueItemsWrapper);

        if ($lastItemId == -1) {
            return;
        }
        $fields = array_keys($fieldsMap);
        $sql = $this->getBaseInsertStatement($table, $fields);

        for ($i = $firstItemId, $j = 0; $i <= $lastItemId; $i++, $j++) {
            $uniqueItemsWrapper[$j]->fields[$key] = new Field($key, $i, NULL);
        }

        $sql .= $this->parseItems($uniqueItemsWrapper, $fieldsMap);

        $this->executeUpdate($sql, $fieldsMap);
    }

    public function copyArray($array)
    {
        $arrayCopy = array();
        foreach ($array as $key => $value) {
            $arrayCopy[$key] = clone $value;
        }
        return $arrayCopy;
    }

    public static function getProductList($xml, $rootItem)
    {
        if (!strpos($rootItem, "->")) {
            return $xml->$rootItem;
        } else {
            $explode = explode("->", $rootItem);
            for ($i = 0; $i < count($explode); $i++) {
                $var = isset($var) ? $var->$explode[$i] : $xml->$explode[$i];
            }
            return $var;
        }
    }

    private function getSaleIdFromItem($result, $itemWrapper)
    {
        return $itemWrapper->fields["sale_id"]->getValue();
    }

    private function getCategoryIdFromItem($result, $itemWrapper)
    {
        return $itemWrapper->fields["category_id"]->getValue();
    }

    private function downloadImage($result, $itemWrapper)
    {
        echo "Downloading image: " . $result . "\n";
        $pathinfo = pathinfo(parse_url($result, PHP_URL_PATH));
        $imageName = uniqid("img_") . "." . $pathinfo["extension"];
        $img = $this->downloadFilesFolder . $imageName;
        if (!file_put_contents(urldecode($img), file_get_contents($result))) {
            return "";
        };
        return $imageName;
    }

    private function processDate($result, $itemWrapper)
    {
        if (strpos($result, "T")) {
            return substr($result, 0, 10);
        }
        return $result;
    }

    private function getFileExtension($result, $itemWrapper)
    {
        $pathinfo = pathinfo(parse_url($result, PHP_URL_PATH));
        return $pathinfo["extension"];
    }

    private function getCurrentDate() {
        return date("Y.m.d");
    }

    /**
     * @param $items
     * @return array
     */
    private function processDependencies($items)
    {
        $data = array();
        foreach ($items as $databaseField => $item) {
            if (null !== $item->getDependsOn()) {
                $value = $this->processItem($item, $items);
            }
            $data[] = "\"" . mysqli_real_escape_string($this->connection, trim($item->getValue())) . "\"";
        }
        return $data;
    }

    private function processItem($dependingItem, $items)
    {
        foreach ($items as $item) {
            if ($dependingItem->getDependsOn() == $item->getDatabaseField()) {
                $dependingItem->setValue($this->getValueBasedOnDependencyField($dependingItem, $item));
            }
        }
        return $dependingItem->getValue();
    }

    private function getValueBasedOnDependencyField($dependingItem, $dependentItem)
    {
        $databaseField = $dependingItem->getDatabaseField();
        if ($databaseField == "sales_desc") {
            return $dependingItem->getValue() . "<br><br>Price:$" . $dependentItem->getValue() . "<retail>";
        } else if ($databaseField == "not_sure") {
            if (null == $dependentItem->getValue() || trim($dependentItem->getValue()) == "") {
                return "1";
            }
        }
        return $dependingItem->getValue();
    }

    private function getDefaultValue($value, $default)
    {
        if (isset($value) && trim($value) != "") {
            return $value;
        }
        return $default;
    }

    /**
     * @param $sql
     * @return string
     */
    private function executeUpdate($sql, $fieldsMap)
    {
        $sql = substr($sql, 0, -2);
        $sql .= $this->getFinishInsertStatement($fieldsMap);
        echo $sql . "\n";
        $this->executeQuery($sql);
        return $sql;
    }

    /**
     * @param $xmlColumnMappingList
     * @param $xmlFeedDAO
     * @return array
     */
    public static function getFieldsMap($xmlColumnMappingList, $xmlFeedDAO)
    {
        $fieldsMap = Array();

        foreach ($xmlColumnMappingList as $mapping) {
            $arr = Array(Type::TYPE => $mapping["XmlValueType"],
                "value" => $mapping["XmlField"]);
            if (!empty($mapping["ActionId"])) {
                $arr["action"] = $xmlFeedDAO->getActionName($mapping["ActionId"]);
            }
            if (!empty($mapping["Default"])) {
                $arr["default"] = $mapping["Default"];
            }
            if (!empty($mapping["DependsOn"])) {
                $arr["dependsOn"] = $mapping["DependsOn"];
            }
            if ($mapping["IsKey"] == 1) {
                $arr["key"] = true;
            }
            $fieldsMap[$mapping["DatabaseColumn"]] = $arr;
        }
        return $fieldsMap;
    }

    private static function getXmlFilePath($xmlFilePath)
    {
        $pathinfo = pathinfo(parse_url($xmlFilePath, PHP_URL_PATH));
        if (strtolower($pathinfo["extension"]) !== "zip") {
            return $xmlFilePath;
        }

        // assuming file.zip is in the same directory as the executing script.
        $temp_file = tempnam(sys_get_temp_dir(), 'xml_');
        file_put_contents($temp_file, fopen($xmlFilePath, 'r'));

        // get the absolute path to $file
        $path = pathinfo(realpath($temp_file), PATHINFO_DIRNAME);
        $urlPath = parse_url($xmlFilePath, PHP_URL_PATH);
        $file_name = basename($urlPath);

        $zip = new ZipArchive;
        $res = $zip->open($temp_file);
        $stat = $zip->statIndex(0);
        if ($res === TRUE) {
            // extract it to the path we determined above
            set_time_limit(3000);
            $zip->extractTo($path);
            $zip->close();
//            echo "$temp_file extracted to $path : " . basename($stat['name']);
        } else {
//            echo "Couldn't open $temp_file";
            die(self::ERROR_READ_XML_FILE . " $xmlFilePath");
        }
        return $path . DIRECTORY_SEPARATOR . basename($stat['name']);
    }
}

class Field
{
    private $databaseField;
    private $value;
    private $dependsOn;

    function __construct($databaseField, $value, $dependsOn)
    {
        $this->databaseField = $databaseField;
        $this->value = $value;
        $this->dependsOn = $dependsOn;
    }

    /**
     * @return mixed
     */
    public function getDatabaseField()
    {
        return $this->databaseField;
    }

    /**
     * @param mixed $databaseField
     */
    public function setDatabaseField($databaseField)
    {
        $this->databaseField = $databaseField;
    }

    /**
     * @return mixed
     */
    public function getDependsOn()
    {
        return $this->dependsOn;
    }

    /**
     * @param mixed $dependsOn
     */
    public function setDependsOn($dependsOn)
    {
        $this->dependsOn = $dependsOn;
    }

    /**
     * @return mixed
     */
    public function getValue()
    {
        return $this->value;
    }

    /**
     * @param mixed $value
     */
    public function setValue($value)
    {
        $this->value = $value;
    }
}

class ValueType
{
    const VALUE = "value";
    const ATTRIBUTE = "attribute";
    const TAG = "tag";
    const TAG_INDEX = "tag_index";
}

class Type
{
    const VALUE = "value";
    const TYPE = "type";
    const DEPENDS_ON = "dependsOn";
}

//you can map column to some static value like ""
class Constants
{
    const FIELDS = "fields";
    const TBL_SALES = "tbl_sales";
    const TB_SALES_IMAGE = "tb_sales_image";
    const IMAGES_FOLDER = './images/';
    const TBL_SALES_ID = "sales_id";
    const TB_SALES_CATEGORY = "tb_sales_category";
}

class ItemWrapper
{
    public $item;
    public $fields;

    function __construct($item, $fields)
    {
        $this->item = $item;
        $this->fields = $fields;
    }

    /**
     * @param mixed $fields
     */
    public function setFields($fields)
    {
        $this->fields = $fields;
    }
}

?>